/*
 * vendor/amlogic/media/common/ge2d/ge2dgen.c
 *
 * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

/* Amlogic Headers */
#include <linux/amlogic/media/ge2d/ge2d.h>

static inline void _set_src1_format(struct ge2d_src1_data_s *src1_data_cfg, struct ge2d_src1_gen_s *src1_gen_cfg,
                                    struct ge2d_dp_gen_s *dp_gen_cfg, unsigned int format_src, unsigned int format_dst)
{
    src1_data_cfg->format_all = format_src;

    src1_data_cfg->format = (format_src >> 8L) & 3L;
    src1_data_cfg->mode_8b_sel = (format_src >> 6L) & 3L;
    src1_data_cfg->lut_en = (format_src >> 5L) & 1;
    src1_data_cfg->sep_en = (format_src >> 2L) & 1;

    src1_data_cfg->endian = (format_src & GE2D_ENDIAN_MASK) >> GE2D_ENDIAN_SHIFT;
    src1_data_cfg->color_map = (format_src & GE2D_COLOR_MAP_MASK) >> GE2D_COLOR_MAP_SHIFT;

    src1_gen_cfg->pic_struct = (format_src >> 3L) & 3L;
    src1_data_cfg->x_yc_ratio = (format_src >> 1) & 1;
    src1_data_cfg->y_yc_ratio = (format_src >> 0) & 1;

    if (format_src & GE2D_FORMAT_DEEP_COLOR) {
        src1_data_cfg->deep_color = 1;
    } else {
        src1_data_cfg->deep_color = 0;
    }

    if ((format_src & GE2D_MATRIX_CUSTOM) || (format_dst & GE2D_MATRIX_CUSTOM)) {
        dp_gen_cfg->use_matrix_default = MATRIX_CUSTOM;
        dp_gen_cfg->conv_matrix_en = 1;
        return;
    }

    if ((format_src & GE2D_FORMAT_YUV) && ((format_dst & GE2D_FORMAT_YUV) == 0)) {
        dp_gen_cfg->use_matrix_default =
            (format_src & GE2D_FORMAT_FULL_RANGE) ? MATRIX_FULL_RANGE_YCC_TO_RGB : MATRIX_YCC_TO_RGB;
        dp_gen_cfg->conv_matrix_en = 1;
    } else if (((format_src & GE2D_FORMAT_YUV) == 0) && (format_dst & GE2D_FORMAT_YUV)) {
        dp_gen_cfg->use_matrix_default =
            (format_dst & GE2D_FORMAT_FULL_RANGE) ? MATRIX_RGB_TO_FULL_RANGE_YCC : MATRIX_RGB_TO_YCC;
        dp_gen_cfg->use_matrix_default |= ((format_dst & GE2D_FORMAT_BT_STANDARD) ? MATRIX_BT_709 : MATRIX_BT_601);
        dp_gen_cfg->conv_matrix_en = 1;
    } else {
        dp_gen_cfg->conv_matrix_en = 0;
    }
}

static inline void _set_src2_format(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
                                    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg, unsigned int format)
{
    src2_dst_data_cfg->src2_format_all = format;

    src2_dst_data_cfg->src2_format = (format >> 8L) & 3L;
    src2_dst_data_cfg->src2_endian = (format & GE2D_ENDIAN_MASK) >> GE2D_ENDIAN_SHIFT;
    src2_dst_data_cfg->src2_color_map = (format & GE2D_COLOR_MAP_MASK) >> GE2D_COLOR_MAP_SHIFT;

    src2_dst_data_cfg->src2_mode_8b_sel = (format >> 6L) & 3L;

    src2_dst_gen_cfg->src2_pic_struct = (format >> 3L) & 3L;
}

static inline void _set_dst_format(struct ge2d_src2_dst_data_s *src2_dst_data_cfg,
                                   struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg, struct ge2d_dp_gen_s *dp_gen_cfg,
                                   unsigned int format_src, unsigned int format_dst)
{
    unsigned int y_yc_ratio;

    src2_dst_data_cfg->dst_format_all = format_dst;
    src2_dst_data_cfg->dst_format = (format_dst >> 8L) & 3L;
    src2_dst_data_cfg->dst_endian = (format_dst & GE2D_ENDIAN_MASK) >> GE2D_ENDIAN_SHIFT;
    src2_dst_data_cfg->dst_color_map = (format_dst & GE2D_COLOR_MAP_MASK) >> GE2D_COLOR_MAP_SHIFT;

    src2_dst_data_cfg->dst_mode_8b_sel = (format_dst >> 6L) & 3L;
    src2_dst_gen_cfg->dst_pic_struct = (format_dst >> 3L) & 3L;

    y_yc_ratio = (format_dst >> 0) & 1;

    if ((format_dst & GE2D_FORMAT_YUV) && ((src2_dst_data_cfg->dst_color_map | 1) == 15L)) {
        src2_dst_data_cfg->dst_format = 0;
        src2_dst_data_cfg->dst_mode_8b_sel = 0;
        src2_dst_data_cfg->dst2_pixel_byte_width = 1;
        if (y_yc_ratio == 0) {
            src2_dst_data_cfg->dst2_discard_mode = 0xc;
        } else {
            src2_dst_data_cfg->dst2_discard_mode = 0xf;
        }
        src2_dst_data_cfg->dst2_enable = 1;
        src2_dst_data_cfg->dst2_color_map = src2_dst_data_cfg->dst_color_map - 5L;
    } else {
        src2_dst_data_cfg->dst2_enable = 0;
    }
    /* #endif */

    if ((format_src & GE2D_MATRIX_CUSTOM) || (format_dst & GE2D_MATRIX_CUSTOM)) {
        dp_gen_cfg->use_matrix_default = MATRIX_CUSTOM;
        dp_gen_cfg->conv_matrix_en = 1;
        return;
    }

    if ((format_src & GE2D_FORMAT_YUV) && ((format_dst & GE2D_FORMAT_YUV) == 0)) {
        dp_gen_cfg->use_matrix_default =
            (format_src & GE2D_FORMAT_FULL_RANGE) ? MATRIX_FULL_RANGE_YCC_TO_RGB : MATRIX_YCC_TO_RGB;
        dp_gen_cfg->conv_matrix_en = 1;
    } else if (((format_src & GE2D_FORMAT_YUV) == 0) && (format_dst & GE2D_FORMAT_YUV)) {
        dp_gen_cfg->use_matrix_default =
            (format_dst & GE2D_FORMAT_FULL_RANGE) ? MATRIX_RGB_TO_FULL_RANGE_YCC : MATRIX_RGB_TO_YCC;
        dp_gen_cfg->use_matrix_default |= ((format_dst & GE2D_FORMAT_BT_STANDARD) ? MATRIX_BT_709 : MATRIX_BT_601);
        dp_gen_cfg->conv_matrix_en = 1;
    } else {
        dp_gen_cfg->conv_matrix_en = 0;
    }
}

void ge2dgen_src(struct ge2d_context_s *wq, unsigned int canvas_addr, unsigned int format, unsigned int phy_addr,
                 unsigned int stride)
{
    struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);
    struct ge2d_src1_gen_s *src1_gen_cfg = ge2d_wq_get_src_gen(wq);
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
    struct ge2d_src2_dst_data_s *src2_dst_data_cfg = ge2d_wq_get_dst_data(wq);

    if ((format != src1_data_cfg->format_all) || (canvas_addr != src1_data_cfg->canaddr) ||
        (phy_addr != src1_data_cfg->phy_addr) || (stride != src1_data_cfg->stride)) {
        src1_data_cfg->canaddr = canvas_addr;

        _set_src1_format(src1_data_cfg, src1_gen_cfg, dp_gen_cfg, format, src2_dst_data_cfg->dst_format_all);
        src1_data_cfg->phy_addr = phy_addr;
        src1_data_cfg->stride = stride;
        wq->config.update_flag |= UPDATE_SRC_DATA;
        wq->config.update_flag |= UPDATE_SRC_GEN;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
}

void ge2dgen_antiflicker(struct ge2d_context_s *wq, unsigned long enable)
{
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
    unsigned long enable_tmp = enable;

    enable_tmp = enable_tmp ? 1 : 0;

    if (dp_gen_cfg->antiflick_en != enable_tmp) {
        dp_gen_cfg->antiflick_en = enable_tmp;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
}
void ge2dgen_post_release_src1buf(struct ge2d_context_s *wq, unsigned int buffer)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->src1_buffer = buffer;
    ge2d_cmd_cfg->release_flag |= RELEASE_SRC1_BUFFER;
}

void ge2dgen_post_release_src1canvas(struct ge2d_context_s *wq)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->release_flag |= RELEASE_SRC1_CANVAS;
}

void ge2dgen_post_release_src2buf(struct ge2d_context_s *wq, unsigned int buffer)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->src2_buffer = buffer;
    ge2d_cmd_cfg->release_flag |= RELEASE_SRC2_BUFFER;
}

void ge2dgen_post_release_src2canvas(struct ge2d_context_s *wq)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->release_flag |= RELEASE_SRC2_CANVAS;
}

void ge2dgen_cb(struct ge2d_context_s *wq, int (*cmd_cb)(unsigned int), unsigned int param)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->cmd_cb = cmd_cb;
    ge2d_cmd_cfg->cmd_cb_param = param;
    ge2d_cmd_cfg->release_flag |= RELEASE_CB;
}

void ge2dgen_src2(struct ge2d_context_s *wq, unsigned int canvas_addr, unsigned int format, unsigned int phy_addr,
                  unsigned int stride)
{
    struct ge2d_src2_dst_data_s *src2_dst_data_cfg = ge2d_wq_get_dst_data(wq);
    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);

    if ((format != src2_dst_data_cfg->src2_format_all) || (canvas_addr != src2_dst_data_cfg->src2_canaddr) ||
        (phy_addr != src2_dst_data_cfg->src2_phyaddr) || (stride != src2_dst_data_cfg->src2_stride)) {
        src2_dst_data_cfg->src2_canaddr = canvas_addr;

        _set_src2_format(src2_dst_data_cfg, src2_dst_gen_cfg, format);
        src2_dst_data_cfg->src2_phyaddr = phy_addr;
        src2_dst_data_cfg->src2_stride = stride;
        wq->config.update_flag |= UPDATE_DST_DATA;
        wq->config.update_flag |= UPDATE_DST_GEN;
    }
}

void ge2dgen_dst(struct ge2d_context_s *wq, unsigned int canvas_addr, unsigned int format, unsigned int phy_addr,
                 unsigned int stride)
{
    struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);
    struct ge2d_src2_dst_data_s *src2_dst_data_cfg = ge2d_wq_get_dst_data(wq);
    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

    if ((format != src2_dst_data_cfg->dst_format_all) || (canvas_addr != src2_dst_data_cfg->dst_canaddr) ||
        (phy_addr != src2_dst_data_cfg->dst_phyaddr) || (stride != src2_dst_data_cfg->dst_stride)) {
        src2_dst_data_cfg->dst_canaddr = canvas_addr;

        _set_dst_format(src2_dst_data_cfg, src2_dst_gen_cfg, dp_gen_cfg, src1_data_cfg->format_all, format);
        src2_dst_data_cfg->dst_phyaddr = phy_addr;
        src2_dst_data_cfg->dst_stride = stride;
        wq->config.update_flag |= UPDATE_DST_DATA;
        wq->config.update_flag |= UPDATE_DST_GEN;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
}

void ge2dgen_src_clip(struct ge2d_context_s *wq, int x, int y, int w, int h)
{
    struct ge2d_src1_gen_s *src1_gen_cfg = ge2d_wq_get_src_gen(wq);
    /* adjust w->x_end h->y_end */
    int w_tmp = w;
    int h_tmp = h;
    w_tmp = x + w_tmp - 1;
    h_tmp = y + h_tmp - 1;
    if (src1_gen_cfg->clipx_start != x || src1_gen_cfg->clipx_end != w_tmp || src1_gen_cfg->clipy_start != y ||
        src1_gen_cfg->clipy_end != h_tmp) {
        src1_gen_cfg->clipx_start = x;
        src1_gen_cfg->clipx_end = w_tmp;
        src1_gen_cfg->clipy_start = y;
        src1_gen_cfg->clipy_end = h_tmp;
        wq->config.update_flag |= UPDATE_SRC_GEN;
    }
}

void ge2dgen_src2_clip(struct ge2d_context_s *wq, int x, int y, int w, int h)
{
    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
    int w_tmp = w;
    int h_tmp = h;

    /* adjust w->x_end h->y_end */
    w_tmp = x + w_tmp - 1;
    h_tmp = y + h_tmp - 1;
    if (src2_dst_gen_cfg->src2_clipx_start != x || src2_dst_gen_cfg->src2_clipx_end != w_tmp ||
        src2_dst_gen_cfg->src2_clipy_start != y || src2_dst_gen_cfg->src2_clipy_end != h_tmp) {
        src2_dst_gen_cfg->src2_clipx_start = x;
        src2_dst_gen_cfg->src2_clipx_end = w_tmp;
        src2_dst_gen_cfg->src2_clipy_start = y;
        src2_dst_gen_cfg->src2_clipy_end = h_tmp;
        wq->config.update_flag |= UPDATE_DST_GEN;
    }
}

void ge2dgen_src_key(struct ge2d_context_s *wq, int en, int key, int keymask, int keymode)
{
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

    if (dp_gen_cfg->src1_key_en != en || dp_gen_cfg->src1_key != key || dp_gen_cfg->src1_key_mask != keymask ||
        dp_gen_cfg->src1_key_mode != keymode) {
        dp_gen_cfg->src1_key_en = en & 0x1;
        dp_gen_cfg->src1_key = key;
        dp_gen_cfg->src1_key_mask = keymask;
        dp_gen_cfg->src1_key_mode = keymode & 0x1;

        dp_gen_cfg->src1_vsc_bank_length = 4L;
        dp_gen_cfg->src1_hsc_bank_length = 4L;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
}
EXPORT_SYMBOL(ge2dgen_src_key);

void ge2dgent_src_gbalpha(struct ge2d_context_s *wq, unsigned char alpha1, unsigned char alpha2)
{
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);
#ifdef CONFIG_GE2D_SRC2
    if ((dp_gen_cfg->src1_gb_alpha != alpha1) || (dp_gen_cfg->src2_gb_alpha != alpha2)) {
        dp_gen_cfg->src1_gb_alpha = alpha1;
        dp_gen_cfg->src2_gb_alpha = alpha2;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
#else
    if (dp_gen_cfg->src1_gb_alpha != alpha1) {
        dp_gen_cfg->src1_gb_alpha = alpha1;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
#endif
}

void ge2dgen_src_color(struct ge2d_context_s *wq, unsigned int color)
{
    struct ge2d_src1_data_s *src1_data_cfg = ge2d_wq_get_src_data(wq);

    if (src1_data_cfg->def_color != color) {
        src1_data_cfg->def_color = color;
        wq->config.update_flag |= UPDATE_SRC_DATA;
    }
}

void ge2dgen_rendering_dir(struct ge2d_context_s *wq, int src_x_dir, int src_y_dir, int dst_x_dir, int dst_y_dir,
                           int dst_xy_swap)
{
    struct ge2d_cmd_s *ge2d_cmd_cfg = ge2d_wq_get_cmd(wq);

    ge2d_cmd_cfg->src1_x_rev = src_x_dir;
    ge2d_cmd_cfg->src1_y_rev = src_y_dir;
    ge2d_cmd_cfg->dst_x_rev = dst_x_dir;
    ge2d_cmd_cfg->dst_y_rev = dst_y_dir;
    ge2d_cmd_cfg->dst_xy_swap = dst_xy_swap;
}

void ge2dgen_dst_clip(struct ge2d_context_s *wq, int x, int y, int w, int h, int mode)
{
    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
    int w_tmp = w;
    int h_tmp = h;
    /* adjust w->x_end h->y_end */
    w_tmp = x + w_tmp - 1;
    h_tmp = y + h_tmp - 1;
    if (src2_dst_gen_cfg->dst_clipx_start != x || src2_dst_gen_cfg->dst_clipx_end != w_tmp ||
        src2_dst_gen_cfg->dst_clipy_start != y || src2_dst_gen_cfg->dst_clipy_end != h_tmp ||
        src2_dst_gen_cfg->dst_clip_mode != mode) {
        src2_dst_gen_cfg->dst_clipx_start = x;
        src2_dst_gen_cfg->dst_clipx_end = w_tmp;
        src2_dst_gen_cfg->dst_clipy_start = y;
        src2_dst_gen_cfg->dst_clipy_end = h_tmp;
        src2_dst_gen_cfg->dst_clip_mode = mode;
        wq->config.update_flag |= UPDATE_DST_GEN;
    }
}

void ge2dgent_src2_clip(struct ge2d_context_s *wq, int x, int y, int w, int h)
{
    struct ge2d_src2_dst_gen_s *src2_dst_gen_cfg = ge2d_wq_get_dst_gen(wq);
    int w_tmp = w;
    int h_tmp = h;
    /* adjust w->x_end h->y_end */
    w_tmp = x + w_tmp - 1;
    h_tmp = y + h_tmp - 1;
    if (src2_dst_gen_cfg->src2_clipx_start != x || src2_dst_gen_cfg->src2_clipx_end != w_tmp ||
        src2_dst_gen_cfg->src2_clipy_start != y || src2_dst_gen_cfg->src2_clipy_end != h_tmp) {
        src2_dst_gen_cfg->src2_clipx_start = x;
        src2_dst_gen_cfg->src2_clipx_end = w_tmp;
        src2_dst_gen_cfg->src2_clipy_start = y;
        src2_dst_gen_cfg->src2_clipy_end = h_tmp;
        wq->config.update_flag |= UPDATE_DST_GEN;
    }
}

void ge2dgen_const_color(struct ge2d_context_s *wq, unsigned int color)
{
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

    if (dp_gen_cfg->alu_const_color != color) {
        dp_gen_cfg->alu_const_color = color;
        wq->config.update_flag |= UPDATE_DP_GEN;
    }
}

void ge2dgen_disable_matrix(struct ge2d_context_s *wq)
{
    struct ge2d_dp_gen_s *dp_gen_cfg = ge2d_wq_get_dp_gen(wq);

    dp_gen_cfg->conv_matrix_en = 0;
    wq->config.update_flag |= UPDATE_DP_GEN;
}
